/*
** $身份: 嘞utf8库.c $
** 操纵 UTF-8 的标准库
** 见版权通知 炉啊.h
*
* * 本人所用声母表: a啊 b哔 c西 d迪 e鹅 f弗 g哥 
* h喝 i艾 j鸡 k颗 l嘞 m摸 n恩
* o欧 p匹 q气 r日 s丝 t嚏 
* u由 v微 w屋 x斯 y医 z只
*
* 一些英文单词缩写,我的翻译大多来自;有道词典,谷歌翻译,百度.
* 一些术语实在不好翻译,所以就原封不动.
* 
* 一些特有的表示特有库的术语是无法翻译的 *
* 否则编译器不能识别不能通过,第一版汉化就行不通.
*
* 
*/

#define 嘞utf8库_西
#define 炉啊_库

#include "嘞前缀.h"

#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "炉啊.h"

#include "嘞辅库.h"
#include "炉啊库.h"

#define 最大统一码	0x10FFFFu

#define 最大UTF		0x7FFFFFFFu

/*
** 已解码UTF-8值的整数类型; 最大UTF需要 31 位.
*/
#if (无符整型_最大 >> 30) >= 1
typedef unsigned int utf整型;
#else
typedef unsigned long utf整型;
#endif

#define 是否内容(匹)	((*(匹) & 0xC0) == 0x80)

/* 来自串库 */
/* 翻译一个相对的串位置: 否定的意思是从终末回去 */
static 炉啊_整数 由_相对位置 (炉啊_整数 位置, size_t 长度) {
  if (位置 >= 0) return 位置;
  else if (0u - (size_t)位置 > 长度) return 0;
  else return (炉啊_整数)长度 + 位置 + 1;
}

/*
** 解码一个UTF-8队列, 若字节队列无效则返回NULL.
*  数组 '极限' 存储每个队列长度的最小值,
*   检查是否表示过长. 
*   它的第一个条目强制一个没有延续字节的非ascii字节错误(计数 == 0).
*/
static const char *utf8_解码 (const char *丝, utf整型 *值, int 严格) {
  static const utf整型 极限[] =
        {~(utf整型)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
  unsigned int 西 = (unsigned char)丝[0];
  utf整型 结果 = 0;  /* 最终结果 */
  if (西 < 0x80)  /* ascii? */
    结果 = 西;
  else {
    int 计数 = 0;  /* 计数延续字节的数目 */
    for (; 西 & 0x40; 西 <<= 1) {  /* 而它需要延续字节... */
      unsigned int 西西 = (unsigned char)丝[++计数];  /* 读下一个字节 */
      if ((西西 & 0xC0) != 0x80)  /* 不是延续字节? */
        return NULL;  /* 无效的字节队列 */
      结果 = (结果 << 6) | (西西 & 0x3F);  /* 从 计数.字节 加低6位 */
    }
    结果 |= ((utf整型)(西 & 0x7F) << (计数 * 5));  /* 添加第一个字节 */
    if (计数 > 5 || 结果 > 最大UTF || 结果 < 极限[计数])
      return NULL;  /* 无效的字节队列 */
    丝 += 计数;  /* 跳过读取的延续字节 */
  }
  if (严格) {
    /* 检查无效的代码点; 太大号或替代品 */
    if (结果 > 最大统一码 || (0xD800u <= 结果 && 结果 <= 0xDFFFu))
      return NULL;
  }
  if (值) *值 = 结果;
  return 丝 + 1;  /* +1 包括第一个字节 */
}

/*
** utf8长度(丝 [, 艾 [, 鸡 [, 不严格]]]) --> 从[艾,鸡]范围开始的印刻号码,
*  或者 零 + 当前位置，如果 '丝' 在区间内没有很好成形,
*/
static int utf长度 (炉啊_国 *嘞) {
  炉啊_整数 恩 = 0;  /* 计数器的字符数 */
  size_t 长度;  /* 以字节为单位的字符串长度 */
  const char *丝 = 炉啊嘞_检查嘞串(嘞, 1, &长度);
  炉啊_整数 位置艾 = 由_相对位置(炉啊嘞_选择整数(嘞, 2, 1), 长度);
  炉啊_整数 位置鸡 = 由_相对位置(炉啊嘞_选择整数(嘞, 3, -1), 长度);
  int 不严格 = 炉啊_到布尔(嘞, 4);
  炉啊嘞_实参检查(嘞, 1 <= 位置艾 && --位置艾 <= (炉啊_整数)长度, 2,
                   "初始位置出界");
  炉啊嘞_实参检查(嘞, --位置鸡 < (炉啊_整数)长度, 3,
                   "最终位置出界");
  while (位置艾 <= 位置鸡) {
    const char *丝1 = utf8_解码(丝 + 位置艾, NULL, !不严格);
    if (丝1 == NULL) {  /* 转换错误? */
      炉啊嘞_推失败(嘞);  /* 返回失败 ... */
      炉啊_推整数(嘞, 位置艾 + 1);  /* ... 和当前位置 */
      return 2;
    }
    位置艾 = 丝1 - 丝;
    恩++;
  }
  炉啊_推整数(嘞, 恩);
  return 1;
}

/*
** 代码点(丝, [艾, [鸡 [, 不严格]]]) -> 返回从[艾,鸡]范围开始的所有字符的代码点.
*/

static int 代码点 (炉啊_国 *嘞) {
  size_t 长度;
  const char *丝 = 炉啊嘞_检查嘞串(嘞, 1, &长度);
  炉啊_整数 位置艾 = 由_相对位置(炉啊嘞_选择整数(嘞, 2, 1), 长度);
  炉啊_整数 位置鹅 = 由_相对位置(炉啊嘞_选择整数(嘞, 3, 位置艾), 长度);
  int 不严格 = 炉啊_到布尔(嘞, 4);
  int 恩;
  const char *丝鹅;
  炉啊嘞_实参检查(嘞, 位置艾 >= 1, 2, "越界");
  炉啊嘞_实参检查(嘞, 位置鹅 <= (炉啊_整数)长度, 3, "越界");
  if (位置艾 > 位置鹅) return 0;  /* 空的时间间隔; 返回没有值 */
  if (位置鹅 - 位置艾 >= 整型_最大)  /* (炉啊_整数 -> 整型) 溢出? */
    return 炉啊嘞_错误(嘞, "串切片太长");
  恩 = (int)(位置鹅 -  位置艾) + 1;  /* 返回数的上限 */
  炉啊嘞_检查栈(嘞, 恩, "串切片太长");
  恩 = 0;  /* 计数返回的次数 */
  丝鹅 = 丝 + 位置鹅;  /* 串结束 */
  for (丝 += 位置艾 - 1; 丝 < 丝鹅;) {
    utf整型 代码;
    丝 = utf8_解码(丝, &代码, !不严格);
    if (丝 == NULL)
      return 炉啊嘞_错误(嘞, "无效的 UTF-8 代码");
    炉啊_推整数(嘞, 代码);
    恩++;
  }
  return 恩;
}

static void 推utf印刻 (炉啊_国 *嘞, int 实参) {
  炉啊_无符 代码 = (炉啊_无符)炉啊嘞_检查整数(嘞, 实参);
  炉啊嘞_实参检查(嘞, 代码 <= 最大UTF, 实参, "值超出范围");
  炉啊_推弗串(嘞, "%U", (long)代码);
}


/*
** utf印刻(恩1, 恩2, ...)  -> 印刻(恩1)..印刻(恩2)...
*/
static int utf印刻 (炉啊_国 *嘞) {
  int 恩 = 炉啊_取顶部(嘞);  /* 实参数目 */
  if (恩 == 1)  /* 优化单印刻的常见情况 */
    推utf印刻(嘞, 1);
  else {
    int 艾;
    炉啊嘞_缓冲 哔;
    炉啊嘞_缓冲初始化(嘞, &哔);
    for (艾 = 1; 艾 <= 恩; 艾++) {
      推utf印刻(嘞, 艾);
      炉啊嘞_加值(&哔);
    }
    炉啊嘞_推结果(&哔);
  }
  return 1;
}

/*
** 偏移(丝, 恩, [艾])  -> 计数从位置 艾 开始的第 恩 个印刻的索引;
* 0表示印刻在 艾 处.
*/

static int 字节偏移 (炉啊_国 *嘞) {
  size_t 长度;
  const char *丝 = 炉啊嘞_检查嘞串(嘞, 1, &长度);
  炉啊_整数 恩  = 炉啊嘞_检查整数(嘞, 2);
  炉啊_整数 位置艾 = (恩 >= 0) ? 1 : 长度 + 1;
  位置艾 = 由_相对位置(炉啊嘞_选择整数(嘞, 3, 位置艾), 长度);
  炉啊嘞_实参检查(嘞, 1 <= 位置艾 && --位置艾 <= (炉啊_整数)长度, 3,
                   "位置出界");
  if (恩 == 0) {
    /* 查找当前字节序列的开头 */
    while (位置艾 > 0 && 是否内容(丝 + 位置艾)) 位置艾--;
  }
  else {
    if (是否内容(丝 + 位置艾))
      return 炉啊嘞_错误(嘞, "初始位置是一个延续字节");
    if (恩 < 0) {
       while (恩 < 0 && 位置艾 > 0) {  /* 向后移动 */
         do {  /* 找到上一个印刻的开头 */
           位置艾--;
         } while (位置艾 > 0 && 是否内容(丝 + 位置艾));
         恩++;
       }
     }
     else {
       恩--;  /* 第一个印刻不移动 */
       while (恩 > 0 && 位置艾 < (炉啊_整数)长度) {
         do {  /* 找出下一个印刻的开头 */
           位置艾++;
         } while (是否内容(丝 + 位置艾));  /* (不能通过最后的 '\0') */
         恩--;
       }
     }
  }
  if (恩 == 0)  /* 它找到给定的印刻了吗? */
    炉啊_推整数(嘞, 位置艾 + 1);
  else  /* 没有这样的印刻 */
    炉啊嘞_推失败(嘞);
  return 1;
}

static int 迭代_辅 (炉啊_国 *嘞, int 严格的) {
  size_t 长度;
  const char *丝 = 炉啊嘞_检查嘞串(嘞, 1, &长度);
  炉啊_整数 恩 = 炉啊_到整数(嘞, 2) - 1;
  if (恩 < 0)  /* 第一次迭代? */
    恩 = 0;  /* 从这里开始 */
  else if (恩 < (炉啊_整数)长度) {
    恩++;  /* 跳过当前字节 */
    while (是否内容(丝 + 恩)) 恩++;  /* 和它的延续 */
  }
  if (恩 >= (炉啊_整数)长度)
    return 0;  /* 没有更多代码点 */
  else {
    utf整型 代码;
    const char *下一个 = utf8_解码(丝 + 恩, &代码, 严格的);
    if (下一个 == NULL)
      return 炉啊嘞_错误(嘞, "无效的 UTF-8 代码");
    炉啊_推整数(嘞, 恩 + 1);
    炉啊_推整数(嘞, 代码);
    return 2;
  }
}

static int 迭代_辅严格 (炉啊_国 *嘞) {
  return 迭代_辅(嘞, 1);
}

static int 迭代_辅松懈 (炉啊_国 *嘞) {
  return 迭代_辅(嘞, 0);
}


static int 迭代_代码 (炉啊_国 *嘞) {
  int 松懈 = 炉啊_到布尔(嘞, 2);
  炉啊嘞_检查串(嘞, 1);
  炉啊_推西函数(嘞, 松懈 ? 迭代_辅松懈 : 迭代_辅严格);
  炉啊_推值(嘞, 1);
  炉啊_推整数(嘞, 0);
  return 3;
}

/* 模式匹配单个 UTF-8 印刻 */
#define UTF8模式	"[\0-\x7F\xC2-\xFD][\x80-\xBF]*"

static const 炉啊嘞_寄存 函数[] = {
  {"偏移", 字节偏移},
  {"代码点", 代码点},
  {"印刻", utf印刻},
  {"长度", utf长度},
  {"代码", 迭代_代码},
  /* 占位符 */
  {"印刻模式", NULL},
  {NULL, NULL}
};

炉啊模_应用程序接口 int 炉啊打开_utf8 (炉啊_国 *嘞) {
  炉啊嘞_新库(嘞, 函数);
  炉啊_推嘞串(嘞, UTF8模式, sizeof(UTF8模式)/sizeof(char) - 1);
  炉啊_设置字段(嘞, -2, "印刻模式");
  return 1;
}